#include "lib.h"

/*
 * Processa uma paticao do texto (divide o texto em palavras, insere essas no hash e tenta gera-las aleatoriamente)
 * parametros:
 *  particao: particao do texto a ser processada
 *  porcentagem: porcentagem de palavras a serem encontradas no texto
 */
void processaParticao(char *particao, int porcentagem){
  char   *token, /* parte da string a ser analisada */ 
    *listaTokens = " ", /* lista de tokens para dividir a string */
    *saveptr; /* ponteiro que salva a parte da string que esta sendo processada pelo strtok_r */
  int i, j, k, nPalavras, tamanhoPalavra, tamanhoParticao = strlen(particao), *nFilhos, *idMaesEncontradas, nEncontradas;
  char aux[6], **maes, c;
  unsigned int seed;
  hash_set set;

  /* cria um has para armazenar as palavras da particao */
  criar_hash_set(&set, 100000);

  /* conta o numero de palavras na particao */
  for(i = nPalavras = 1; i < tamanhoParticao; ++i){
    /* conta o numero de espacos para achar o numero de palavras*/
    if(particao[i] == ' '){
      ++nPalavras;
    }
  }
  
  /* aloca o vetor de palavras maes e o vetor de numero de filhos de cada mae */
  maes = (char **)malloc(nPalavras * sizeof(char *));
  nFilhos = (int *)malloc(nPalavras * sizeof(int));

  /* busca o primeiro token */
  token = strtok_r(particao, listaTokens, &saveptr);

  /* continua dividindo a string ate que ela seja totalmente processada */
  i = 0;
  while(token != NULL){
    maes[i] = (char *)malloc(strlen(token) * sizeof(char)); /* aloca a palavra mae*/
    nFilhos[i] = 0; /* seta o numero de filho para 0 */
    strcpy(maes[i], token); /* copia a palavra para a lista */
    
    tamanhoPalavra = strlen(token); /* tamanho da palavra mae */
    /* enquanto o tamanho da palavra resta for maior que 5 ela sera dividida */
    while(tamanhoPalavra > 5){
      c = token[5]; /* copia o caractere de divisao da palavra */
      token[5] = '\0'; /* divide a string */
      strcpy(aux, token); /* copia a parte da palavra */
      
      /* insere a parte da palavra no hash */
      if(inserir_hash_set_mae(&set, aux, i)){
        ++(nFilhos[i]);
      }
      
      /* volta o caractere */
      token[5] = c;
      tamanhoPalavra -= 5;
      token = token + 5;  
    }
    
    if(inserir_hash_set_mae(&set, aux, i)){
      ++(nFilhos[i]);
    }
    token = strtok_r(NULL, listaTokens, &saveptr); /* busca o proximo token */
    
    ++i;
  }

  i = 0; /* seta o contador de palavras geradas para 0 */
  
  /* gera a porcentagem desejada de palavras */
  while(i < porcentagem*nPalavras/100){
    /* gera incrementalmente palavras de 1 a 5 caracteres */
    for(j = 0; j < 5; ++j){
      aux[j] = 'a' + rand_r(&seed) % 26; /* gera o proximo caractere da palavra */
      aux[j+1] = '\0';
      
      nEncontradas = encontrar_hash_set(&set, aux, &idMaesEncontradas); /* busca a palavra no hash */
      
      /* se a palavra foi encontrada marca todas a palavras maes que contem essa palavra */
      if(nEncontradas != 0){
        for(k = 0; k < nEncontradas; ++k){
          --(nFilhos[idMaesEncontradas[k]]);
          /*verifica se uma das palavras maes esta completa */
          if(nFilhos[idMaesEncontradas[k]] == 0){
            ++i;
          }
        }
      }
    }
  }
  
  /* desaloca memoria */
  for(i = 0; i < nPalavras; ++i){
    free(maes[i]);
  }
  free(maes);
  free(nFilhos);
}

/*
 * Recebe uma particao do texto e divide seu processamento entre os nucleos do no
 * Parametros:
 *  buffer: particao do texto a ser processada
*/
void rodar(char *buffer, int porcentagem, char *nomeComputador){
  int nProcessadores = omp_get_num_procs(), cntProcessadores;
  char **particoes;
  int inicioParticao, fimParticao;
    
  /* aloca o vetor de particoes */
  particoes = (char **)malloc(sizeof(char *) * nProcessadores);
  
  /* divide o texto em particoes e envia para os processos */
  inicioParticao = fimParticao = 0;
  for(cntProcessadores = 0; cntProcessadores < nProcessadores - 1; ++cntProcessadores){
    /* calcula o fim da particao */
    fimParticao = ((float)strlen(buffer) / nProcessadores) * (cntProcessadores + 1);
    
    /* ajusta o fim da particao para nao dividir uma palavra */
    while(buffer[fimParticao] != ' '){
      ++fimParticao;
    }

    buffer[fimParticao] = '\0'; /* marca o fim da particao */

    /* aloca a nova particao e copia o texto para ela */
    particoes[cntProcessadores] = (char *)malloc(sizeof(char) * (strlen(&buffer[inicioParticao]) + 1));
    strcpy(particoes[cntProcessadores], &buffer[inicioParticao]);      

    buffer[fimParticao] = ' ';

    /* calcula o inicio da proxima particao */
    inicioParticao = fimParticao;
  }

  particoes[cntProcessadores] = (char *)malloc(sizeof(char) * (strlen(&buffer[inicioParticao]) + 1));
  strcpy(particoes[cntProcessadores], &buffer[inicioParticao]);
    
  /* cria uma regiao paralela */
  #pragma omp parallel shared(particoes)
  {
    /* faz cada thread processar uma particao do texto (em paralelo) */
    #pragma omp for schedule(static) private(cntProcessadores)
    for(cntProcessadores = 0; cntProcessadores < nProcessadores; ++cntProcessadores){
      /* processa a particao e retorna o numero de palindromos e de primos */
      processaParticao(particoes[cntProcessadores], porcentagem);
    
      /* desaloca a particao de foi processada */
      free(particoes[cntProcessadores]);
    
      printf("[%s] -> Particao %d pela thread %d.\n",
        nomeComputador, 
        cntProcessadores,  
        omp_get_thread_num());
    }
  }
  
  /* desaloca o vetor de particoes */
  free(particoes);
}


/*
 * Converte uma string para minuscula
 * parametros:
 * 	str: string a ser convertida
 */
void minuscula(char *str){
	int i;

	/* percorre a string */
	for(i = 0; str[i] != '\0'; ++i){
		/* se o caractere eh uma letra maiuscula faz a conversao */	
		if(str[i] >= 'A' && str[i] <= 'Z'){
			str[i] += ('a' - 'A');
		}
	}
}

/*
 * Remove caracateres nao alfanumericos de uma string
 * parametros:
 * 	str: string a ser processada
 * retorno:
 * 	se a string eh vazia ou nao
 */
int limpa(char *str){
	int 	i, /* contador para percorrer a string */ 
		limpo; /* comprimento da string limpa */

	limpo = 0;

	/* percorre a string ate o final */
	for(i = 0; str[i] != '\0'; ++i){
		/* se encontrar um caracter alfanumerico coloca na parte limpa da string */
		if((str[i] >= 'a' && str[i] <= 'z') ||
			(str[i] >= 'A' && str[i] <= 'Z')){	
			str[limpo] = str[i];
			++limpo;
		}
	}
	str[limpo] = '\0';

	/* retorna se a string eh vazia ou nao */
	return(limpo != 0);
}

